package cn.csc.framework.redisson.lock.aspect;


import cn.csc.framework.common.exception.ServiceException;
import cn.csc.framework.redisson.lock.annotation.Lock;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.util.concurrent.TimeUnit;

@Aspect
@RequiredArgsConstructor
@Slf4j
public class LockInterceptor {


    private final RedissonClient redisson;


    @Around("@annotation(cn.csc.framework.redisson.lock.annotation.Lock)")
    public Object around(JoinPoint joinPoint) throws Throwable {

        MethodSignature ms = (MethodSignature) joinPoint.getSignature();
        Lock lock = ms.getMethod().getAnnotation(Lock.class);
        String prefix = lock.prefix();
        String[] keys = lock.key();
        if (prefix.equals("")) {
            prefix = joinPoint.getTarget().getClass().getName() + "." + ms.getName();
        }
        StringBuffer key = new StringBuffer();
       // if (!StrUtil.isEmpty(lock.key())) {
        if (!ArrayUtils.isEmpty(lock.key())) {
            //计算key
            for(String kes : keys) {
                ExpressionParser parser = new SpelExpressionParser();
                Expression exp = parser.parseExpression(kes);
                StandardEvaluationContext ctx = new StandardEvaluationContext();
                String[] argNames = ms.getParameterNames();
                //设置变量
                for (int i = 0; i < argNames.length; i++) {
                    ctx.setVariable(argNames[i], joinPoint.getArgs()[i]);
                }
                Object value = exp.getValue(ctx);
                if (value != null) {
                    key.append(":").append(value.toString());
                }
            }
        }
        String lockKey = "lock" + (StrUtil.isEmpty(key) ? prefix : (prefix + ":" + key));
        log.info("lockKey= " + lockKey);
        RLock rlock = redisson.getLock(lockKey);

        try {
            if (!lock.isTry()) {
                rlock.lock();
                return ((ProceedingJoinPoint) joinPoint).proceed();
            } else {
                if (rlock.tryLock(lock.waitTime(), lock.expireTime(), TimeUnit.SECONDS)) {
                    return ((ProceedingJoinPoint) joinPoint).proceed();
                } else {
                    throw new ServiceException(600, null);
                }
            }
        } finally {
            if (rlock.isLocked() && rlock.isHeldByCurrentThread()) {
                rlock.unlock();
            } else {
                log.error("lockKey:" + lockKey + " unlock failure");
            }
        }
    }
}
